package com.aleyn.processor.generator

import com.aleyn.processor.data.RouterMeta
import com.aleyn.processor.scanner.SINGLETON
import com.google.devtools.ksp.processing.CodeGenerator
import com.google.devtools.ksp.processing.KSPLogger
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.FileSpec
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.TypeSpec
import com.squareup.kotlinpoet.jvm.jvmName
import com.squareup.kotlinpoet.jvm.jvmStatic

/**
 * @author : Aleyn
 * @date : 2023/07/23 : 14:47
 *
 * 为 LRouter 提供基于 KSP 的代码生成能力。
 *
 * 该文件包含一系列使用 KotlinPoet 的扩展函数，它们负责将 KSP 在编译期收集到的元数据
 * (`RouterMeta`) 转换成具体的 Kotlin 代码。主要生成两类文件：
 * 1.  **模块路由注册类**: 每个 Gradle 模块会生成一个，用于集中注册该模块下所有的路由 (`@Route`)
 *     和依赖注入定义 (`@Single`, `@Factory` 等)。
 * 2.  **依赖注入辅助类**: 为每个使用了 `@Autowired` 注解的类（通常是 Activity 或 Fragment）生成，
 *     负责在运行时执行依赖注入操作。
 */

/**
 * 生成的模块路由注册类的后缀。
 * 例如，`app` 模块会生成 `app__ModuleRouter__Registered` 类。
 */
internal const val MODULE_ROUTER_CLASS_SUFFIX = "__ModuleRouter__Registered"

/**
 * 生成的依赖注入辅助类的后缀。
 * 例如，`MainActivity` 会生成 `MainActivity__LRouter$$Autowired` 类。
 */
internal const val AUTOWIRED_SUFFIX = "__LRouter\$\$Autowired"

/**
 * 在生成的依赖注入辅助类中，执行注入操作的方法名。
 */
internal const val FUN_INJECT_NAME = "autowiredInject"

/**
 * 生成代码时，用于控制缩进的字符串，以增强可读性。
 */
internal const val LINE_START = "  "

/**
 * 为被 `@Autowired` 注解的类生成依赖注入辅助类。
 *
 * 这个生成的类（例如 `XXXActivity__LRouter$$Autowired`）是一个 `object`，
 * 包含一个静态方法 `autowiredInject(target: Any)`。此方法在运行时被调用，
 * 负责从 `Activity/Fragment` 的 `intent` 或 `arguments` (Bundle) 中提取参数，
 * 并将其赋值给目标对象中被 `@Autowired` 注解的属性。
 *
 * @receiver [RouterMeta.RouterAutowired] 包含生成注入类所需的所有元数据，
 *           例如包名、类名以及需要注入的属性列表。
 * @param codeGenerator KSP 提供的代码生成器实例，用于创建新文件。
 * @param logger KSP 提供的日志记录器实例，用于在编译期输出信息。
 */
fun RouterMeta.RouterAutowired.generatorClass(
    codeGenerator: CodeGenerator,
    logger: KSPLogger,
) {
    val routerAutowired = this
    val pkgName = routerAutowired.pkgName
    val simpleName = routerAutowired.simpleName
    val list = routerAutowired.list

    val parentDeclaration =
        routerAutowired.list.firstOrNull()?.second?.parent as? KSClassDeclaration

    val className = simpleName + AUTOWIRED_SUFFIX

    // 使用 KotlinPoet 构建文件和类
    val fileBuilder = FileSpec.builder(pkgName, className)
        .jvmName(className)
        .addImport("com.aleyn.router.parser", "DefaultParamParser")
        .addImport(pkgName, simpleName)

    val classSpec = TypeSpec.objectBuilder(className)
        .addAnnotation(ClassName.bestGuess("androidx.annotation.Keep"))

    // 处理泛型，确保生成的代码能正确处理带泛型的目标类 (e.g., BaseActivity<T>)
    val typeParams = if (parentDeclaration?.typeParameters.isNullOrEmpty()) "" else
        parentDeclaration!!.typeParameters.joinToString(", ", "<", ">") {
            "*"
        }

    // 构建核心注入方法 `autowiredInject`
    val funSpec = FunSpec.builder(FUN_INJECT_NAME)
        .jvmStatic()
        .addParameter("target", Any::class)
        .beginControlFlow("\\nif (target is ${simpleName}$typeParams)")

    // 遍历所有需要注入的属性，并为每个属性生成相应的解析和赋值代码
    list.forEach { item ->
        val declaration = item.second

        val paramKey = item.first
        val fieldName = declaration.simpleName.asString()
        val type = declaration.type.toString()

        val paramType = declaration.type.resolve()
        val typeDeclaration = paramType.declaration as KSClassDeclaration

        // 导入非基本类型的包名，以避免类找不到的错误
        if (!type.isPrimitiveAndString()) {
            val typePkgName = paramType.declaration.packageName.asString()
            fileBuilder.addImport(typePkgName, type)
        }

        val typeAllName = declaration.getAllTypeStr()

        // 根据属性类型（是否为 Parcelable/Serializable）生成不同的解析逻辑
        // - 对于 Parcelable/Serializable，使用 `DefaultParamParser.parseDefault`
        // - 对于其他类型（包括基本类型、String、以及通过 Json 解析的自定义对象），使用 `DefaultParamParser.parseAny`
        if (typeDeclaration.isParcelable()) {
            val key = paramKey.ifBlank { fieldName }
            funSpec.addCode(
                "\nDefaultParamParser.parseDefault<%1L>(target, \"%2L\", %3L)\\n?.let{ target.%4L = it }\n",
                typeAllName,
                key,
                "$type::class.java",
                fieldName
            )
        } else {
            funSpec.addCode(
                "\nDefaultParamParser.parseAny<%1L>(target, \"%2L\", \"%3L\")\n?.let{ target.%3L = it }\n",
                typeAllName,
                paramKey,
                fieldName,
            )
        }
    }

    funSpec.endControlFlow()
    classSpec.addFunction(funSpec.build())

    logger.info("gen :$pkgName.$className")

    val fileSpec = fileBuilder.addType(classSpec.build()).build()

    // 使用 CodeGenerator 将生成的文件写入磁盘
    codeGenerator.getFile(pkgName, className)
        .bufferedWriter()
        .use { fileSpec.writeTo(it) }
}

/**
 *  为每个 Gradle 模块生成一个模块注册类（例如 `app__ModuleRouter__Registered`）。
 *
 *  这个类是 LRouter 实现模块化和自动注册的关键。它包含两个核心功能：
 *  1. `registerRouter()`: 在一个集中的方法里，调用 `LRouter.registerRoute()`
 *     来注册当前模块中所有被 `@Route` 注解标记的页面/组件。
 *  2. `initDefinition()`: 在另一个方法里，初始化当前模块中所有的依赖注入定义
 *     （`@Single`, `@Factory` 等），将它们添加到 LRouter 的依赖注入容器中。
 *
 *  这些生成的类最终会被 `LRouter` 的 Gradle 插件在编译期扫描到，
 *  并通过字节码插桩技术，在应用启动时自动调用它们的注册方法。
 *
 * @receiver [RouterMeta.Module] 包含当前模块的所有路由和依赖注入元数据。
 * @param codeGenerator KSP 提供的代码生成器实例。
 * @param logger KSP 提供的日志记录器实例。
 * @param moduleName 当前 Gradle 模块的名称，用于生成唯一的类名。
 */
fun RouterMeta.Module.generatorModule(
    codeGenerator: CodeGenerator,
    logger: KSPLogger,
    moduleName: String?
) {
    logger.logging("start generator${moduleName}Module")
    val pkgName = "com.module.router"

    // 如果模块内没有任何路由或注入定义，则无需生成注册文件
    if (this.router.isEmpty() && this.definitions.isEmpty()) return

    val className = moduleName.orEmpty() + MODULE_ROUTER_CLASS_SUFFIX

    val fileBuilder = FileSpec.builder(pkgName, className)
        .jvmName(className)
        .addImport("com.aleyn.router", "LRouter")
        .addImport("com.aleyn.router.core", "RouteMeta")

    // 构建 `registerRouter` 方法
    val routerFunSpec = FunSpec.builder("registerRouter")
        .addAnnotation(ClassName.bestGuess("androidx.annotation.Keep"))

    // 遍历所有路由元数据，为每个路由生成 `LRouter.registerRoute` 调用代码
    router.forEach {
        routerFunSpec.addCode(
            "${LINE_START}RouteMeta(\"%1L\", \"%2L\", %3L, \"%4L\").let(LRouter::registerRoute)\n",
            it.path,
            it.desc,
            it.other,
            it.className,
        )
    }

    val fileSpec = fileBuilder
        .addFunction(routerFunSpec.build())
        .addFunction(genDefinition(fileBuilder)) // 构建并添加 `initDefinition` 方法
        .build()

    // 写入文件
    codeGenerator.getFile(pkgName, className)
        .bufferedWriter()
        .use { fileSpec.writeTo(it) }
}

/**
 * 生成 `initDefinition` 方法，用于注册模块内的所有依赖注入定义。
 *
 * 该方法会遍历模块中所有的 `Definition` 元数据，并为每一个定义生成相应的
 * Kotlin DSL 代码，例如 `single { MyService(get()) }` 或 `factory { Presenter(get()) }`。
 *
 * @receiver [RouterMeta.Module] 包含当前模块的所有依赖注入元数据。
 * @param fileBuilder [FileSpec.Builder] 用于在需要时添加额外的 import 语句。
 * @return [FunSpec] 构建完成的 `initDefinition` 方法。
 */
private fun RouterMeta.Module.genDefinition(fileBuilder: FileSpec.Builder): FunSpec {
    val definitionFun = FunSpec.builder("initDefinition")
    if (this.definitions.isNotEmpty()) {
        this.definitions.forEach {
            // 1. 生成构造函数参数代码，例如 `(get(), get(sq("api")))`
            val paramGet = generateConstructor(it.parameters)
            if (!paramGet.endsWith("()")) {
                fileBuilder.addImport("com.aleyn.router.inject.qualifier", "sq")
            }
            // 2. 处理限定符 (qualifier)，用于区分同一类型的不同实例
            val qualifier = if (it.qualifier.isNullOrBlank()) "" else {
                fileBuilder.addImport("com.aleyn.router.inject.qualifier", "sq")
                "sq(\"${it.qualifier}\")"
            }
            // 3. 处理懒加载 (lazy) 标志，仅对 Singleton 有效
            val lazy = if (it.isType(SINGLETON)) {
                (if (qualifier.isBlank()) "" else ", ") + "lazy = ${it.lazy}"
            } else ""

            fileBuilder.addImport(it.packageName, it.label)
            fileBuilder.addImport("com.aleyn.router.inject", it.keyword.keyword)

            // 4. 处理绑定的父类型 (接口)，例如 `single<ApiService> { ... }`
            val superName = it.binding?.simpleName?.asString()
            var bindCls = ""
            if (it.binding != null && superName != "Any") {
                fileBuilder.addImport(
                    it.binding.packageName.asString(),
                    it.binding.simpleName.asString()
                )
                bindCls = "<${superName}>"
            }

            // 5. 生成最终的注入定义代码，并添加到方法体中
            definitionFun.addCode("$LINE_START${it.keyword.keyword}$bindCls($qualifier$lazy) { ${it.label}$paramGet }\n")
        }
    }
    return definitionFun
        .addAnnotation(ClassName.bestGuess("androidx.annotation.Keep"))
        .build()
}